#include <Precomp.h>
#include <defs.h>
#include <winsock.h>
#include <packet.h>
#include <baseconnection.h>

bool CBaseConnection::m_sNetworkInitialized = false;

CBaseConnection::CBaseConnection() :
    m_socket(INVALID_SOCKET),
    m_wPort(0),
    m_eConnectionStatus(eConnectionStatus_Disconnected),
    m_cbBufferedRead(0),
    m_cbBufferedWrite(0)
{
    m_szServerName[0] = '\0';

    if(!m_sNetworkInitialized)
    {
        Initialize();
    }
}


void CBaseConnection::Initialize()
{
    WORD wVersionRequested;
    WSADATA wsaData;
 
    wVersionRequested = MAKEWORD(1, 1);
 
    if(WSAStartup( wVersionRequested, &wsaData ) == 0)
    {
        
    }
    else
    {
        // random error state
    }

    m_sNetworkInitialized = true;
}


bool CBaseConnection::Connect(const char* szServerName, WORD wPort)
{
    struct sockaddr_in addr;
    struct hostent *hp;

    strcpy_s(m_szServerName, sizeof(m_szServerName), szServerName);
    m_wPort = wPort;

    if ((m_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return false;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(m_wPort);

    if ((addr.sin_addr.s_addr = inet_addr(m_szServerName)) == -1)
    {
        if ((hp = gethostbyname(m_szServerName)) == NULL)
        {
            return false;
        }
        else
        {
            addr.sin_addr.s_addr = *(LONG *) hp->h_addr;
        }
    }

    if (connect(m_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
        return false;
    }

    unsigned long lFlags = 1;

    ioctlsocket(m_socket, FIONBIO, &lFlags);

    m_eConnectionStatus = eConnectionStatus_Connected;

    return true;
}

void CBaseConnection::WriteData(BYTE* pbData, int cbLen)
{
    if(m_eConnectionStatus == eConnectionStatus_Connected)
    {
        if(m_cbBufferedWrite + cbLen > m_cbMaxWriteBuffer)
        {
            m_eConnectionStatus = eConnectionStatus_Error;
            return;
        }

        memcpy(m_rgWriteBuffer + m_cbBufferedWrite, pbData, cbLen);
        m_cbBufferedWrite += cbLen;
    }
}

void CBaseConnection::Update()
{
    if(m_eConnectionStatus == eConnectionStatus_Connected)
    {
        int maxRecv = m_cbMaxReadBuffer - m_cbBufferedRead;
        int cbRecv = recv(m_socket,(char *)(m_rgReadBuffer + m_cbBufferedRead), maxRecv, 0);

        if(cbRecv <= 0 && WSAGetLastError() != WSAEWOULDBLOCK)
        {
            m_eConnectionStatus = eConnectionStatus_Error;
            return;
        }
        else if(cbRecv > 0)
        {
            m_cbBufferedRead = m_cbBufferedRead + cbRecv;
        }

        if(m_cbBufferedWrite > 0)
        {
            int cbSent = send(m_socket, (char*)m_rgWriteBuffer, m_cbBufferedWrite, 0);

            if(cbSent <= 0 && WSAGetLastError() != WSAEWOULDBLOCK)
            {
                m_eConnectionStatus = eConnectionStatus_Error;
                return;
            }

            if(cbSent > 0)
            {
                // Shift all values back. Yes, this is too much work, will update if this is a bottleneck.
                for(int i = cbSent; i < m_cbBufferedWrite; i++)
                {
                    m_rgWriteBuffer[i - cbSent] = m_rgWriteBuffer[i];
                }

                m_cbBufferedWrite -= cbSent;
            }
        }
    }
}

void CBaseConnection::ConsumeReadBuffer(int cbConsume)
{
    int cbLeft = m_cbBufferedRead - cbConsume;

    for(int i = 0; i < cbLeft; i++)
    {
        m_rgReadBuffer[i] = m_rgReadBuffer[i + cbConsume];
    }

    m_cbBufferedRead = cbLeft;
}

